﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;

namespace SimpleModeler.Graphics
{
    /// <summary>
    /// Repräsentiert eine ID für ein SceneObject
    /// </summary>
    [Serializable()]
    public class ID : IComparable
    {
        #region STATIC
        private static ID _nextID;

        /// <summary>
        /// Liefert die nächste ID
        /// </summary>
        internal static ID NextID
        {
            get
            {
                if (_nextID == null) _nextID = new ID(0, 0, 0, 1); // Neue ID erzeugen, wenn noch keine vorhanden ist
                ID id = new ID(_nextID.Color);
                int next = _nextID.Color.ToArgb() + 1;
                if (next == Color.Red.ToArgb() || next == Color.Green.ToArgb() || next == Color.Blue.ToArgb())
                    next++;
                _nextID.Color = Color.FromArgb(next);
                return id;
            }
        }

        /// <summary>
        /// Erzeugt eine ID aus einer Farbe
        /// </summary>
        /// <param name="c">Farbe, welche die ID angibt</param>
        /// <returns>Erzeugte ID</returns>
        public static ID GetIDFromColor(Color c) { return new ID(c); }

        /// <summary>
        /// Erzeugt eine ID aus einer Farbe
        /// </summary>
        /// <param name="a">Alphaanteil. Gültige Werte: 0 bis 255</param>
        /// <param name="r">Rotanteil. Gültige Werte: 0 bis 255</param>
        /// <param name="g">Grünanteil. Gültige Werte: 0 bis 255</param>
        /// <param name="b">Blauanteil. Gültige Werte: 0 bis 255</param>
        /// <returns>Erzeugte ID</returns>
        public static ID GetIDFromColor(byte a, byte r, byte g, byte b) { return new ID(a, r, g, b); }
        
        /// <summary>
        /// Vergleicht zwei ID's miteinander
        /// </summary>
        /// <param name="a">ID a</param>
        /// <param name="b">ID b</param>
        /// <returns>True, wenn die ID's ident sind</returns>
        public static bool operator ==(ID a, ID b)
        {
            // Wenn beide null oder die selbe Instanz
            if (System.Object.ReferenceEquals(a, b))
                return true;
            // Wenn nur eines von beiden null ist, return false
            if (((object)a == null) || ((object)b == null))
                return false;
            return a.Color.ToArgb() == b.Color.ToArgb();
        }
        /// <summary>
        /// Vergleicht zwei ID's miteinander
        /// </summary>
        /// <param name="a">ID a</param>
        /// <param name="b">ID b</param>
        /// <returns>True, wenn die ID's nicht ident sind</returns>
        public static bool operator !=(ID a, ID b) { return !(a == b); }
        #endregion


        #region Properties
        /// <summary>
        /// Farbe, welche die ID des Objektes repräsentiert
        /// </summary>
        public Color Color { get; private set; }
        #endregion // Properties


        #region Constructors
        /// <summary>
        /// Erstellt eine ID aus einem 32-Bit-ARGB-Wert
        /// </summary>
        /// <param name="argb">Ein Wert, der den 32-Bit-ARGB-Wert angibt</param>
        private ID(int argb) : this(Color.FromArgb(argb)) { }

        /// <summary>
        ///     Erstellt eine ID aus den angegebenen 8-Bit-Farbwerten
        ///     (rot, grün und blau).Der Alphawert ist implizit 255 (nicht transparent). Obwohl
        ///     bei dieser Methode die Übergabe eines 32-Bit-Werts für jeden Farbanteil möglich
        ///     wäre, ist der Wert für die einzelnen Anteile auf jeweils 8 Bits beschränkt.
        /// </summary>
        /// <param name="red">Der Wert des Rotanteils für die neue ID. Gültige Werte sind 0 bis 255</param>
        /// <param name="green">Der Wert des Grünanteils für die neue ID. Gültige Werte sind 0 bis 255</param>
        /// <param name="blue">Der Wert des Blauanteils für die neue ID. Gültige Werte sind 0 bis 255</param>
        private ID(byte red, byte green, byte blue) : this(Color.FromArgb(red, green, blue)) { }

        /// <summary>
        ///     Erstellt eine ID aus den vier ARGB-Komponentenwerten
        ///     (Alpha, rot, grün und blau). Obwohl bei dieser Methode die Übergabe eines
        ///     32-Bit-Werts für jeden Komponente möglich wäre, ist der Wert für die einzelnen
        ///     Komponenten auf jeweils 8 Bits beschränkt.
        /// </summary>
        /// <param name="alpha">Der Wert der Alphakomponente für die neue ID. Gültige Werte sind 0 bis 255</param>
        /// <param name="red">Der Wert des Rotanteils für die neue ID. Gültige Werte sind 0 bis 255</param>
        /// <param name="green">Der Wert des Grünanteils für die neue ID. Gültige Werte sind 0 bis 255</param>
        /// <param name="blue">Der Wert des Blauanteils für die neue ID. Gültige Werte sind 0 bis 255</param>
        private ID(byte alpha, byte red, byte green, byte blue) : this(Color.FromArgb(alpha, red, green, blue)) { }

        /// <summary>
        /// Erstellt eine ID aus einer System.Drawing.Color.
        /// </summary>
        /// <param name="c">Color-Objekt</param>
        private ID(Color c) { this.Color = c; }
        #endregion // Constructors


        #region Methods
        /// <summary>
        /// Überprüft ein Objekt mit diesem Objekt auf Gleichheit
        /// </summary>
        /// <param name="obj">Zu überprüfendes Objekt</param>
        /// <returns>Kleiner 0, wenn obj kleiner ist, 0 wenn die Objekte gleich sind, Größer 0 wenn obj größer ist</returns>
        public int CompareTo(object obj)
        {
            Color c;
            if (obj.GetType() == typeof(Color)) c = (Color)obj;
            else if (obj.GetType() == typeof(ID)) c = ((ID)obj).Color;
            else if (obj.GetType() == typeof(Byte4)) c = Color.FromArgb((int)((Byte4)obj).ToUInt32());
            else if (obj.GetType() == typeof(int)) c = Color.FromArgb((int)obj);
            else return -1;
            return this.Color.ToArgb().CompareTo(c.ToArgb());
        }

        /// <summary>
        /// Überprüft ein Objekt gleich diesem Objekt ist
        /// </summary>
        /// <param name="obj">Zu prüfendes Objekt</param>
        /// <returns>True, wenn die Objekte gleich sind</returns>
        public override bool Equals(object obj)
        {
            ID o = obj as ID;
            Color c;
            if ((object)o == null)
            {
                if (obj is Color) c = (Color)obj;
                else return false;
            }
            else
                c = o.Color;
            return this.Color.ToArgb() == c.ToArgb();
        }

        /// <summary>
        /// Gibt einen Hashcode für diese ID zurück
        /// </summary>
        /// <returns>Hashcode</returns>
        public override int GetHashCode() { return this.Color.GetHashCode(); }

        /// <summary>
        /// Stringrepräsentation
        /// </summary>
        /// <returns>Stringrepräsentation</returns>
        public override string ToString() { return this.Color.ToArgb().ToString(); }
        #endregion // Methods
    }
}
